package com.gitee.jjnnzb.designpatterninspring.common.advice;

import com.gitee.jjnnzb.designpatterninspring.common.bean.CommonException;
import com.gitee.jjnnzb.designpatterninspring.common.bean.ErrorEnum;
import com.gitee.jjnnzb.designpatterninspring.common.bean.ResponseBean;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.UnexpectedTypeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.io.FileNotFoundException;

/**
 * 全局异常捕获
 *
 * @author Administrator
 */
@RestControllerAdvice
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@Slf4j
public class GlobalExceptionHandler {
    private static final String EXCEPTION_MESSAGE = "异常";
    private static final String NETWORK_ERROR = "网络异常,请重试";
    
    @ExceptionHandler(value = Exception.class)
    public ResponseBean<String> defaultErrorHandler(Exception e) {
        if (log.isErrorEnabled()) {
            log.error(EXCEPTION_MESSAGE, e);
        }
        return ResponseBean.error(ErrorEnum.SERVER_ERROR.getErrorCode(), NETWORK_ERROR);
    }
    
    @ExceptionHandler(CommonException.class)
    public ResponseBean<String> handleCommonException(CommonException e) {
        if (log.isErrorEnabled()) {
            log.error(EXCEPTION_MESSAGE, e);
        }
        return ResponseBean.error(ErrorEnum.SERVER_ERROR.getErrorCode(), e.getMessage());
    }
    
    @ExceptionHandler(value = {IllegalArgumentException.class, IllegalStateException.class})
    public ResponseBean<String> handleAssertException(Exception e) {
        if (log.isErrorEnabled()) {
            log.error(EXCEPTION_MESSAGE, e);
        }
        return ResponseBean.error(ErrorEnum.SERVER_ERROR.getErrorCode(), e.getMessage());
    }
    
    @ExceptionHandler({BindException.class,                 // 请求参数 绑定到java bean上失败时抛出  关键词 : @Valid 、 Java bean 、表单(Content-Type: multipart/form-data)
            ConstraintViolationException.class,  // 普通参数(非 java bean)校验出错时抛出  关键词 : @Validated 、 非Java bean
            MethodArgumentNotValidException.class// 请求体   绑定到java bean上失败时抛出  关键词 : @Valid 、 @RequestBody、Java bean 、表单(Content-Type: application/json、Content-Type: application/xml)
    })
    public ResponseBean<String> handleBindException(Exception ex) {
        String errorMsg = "参数验证错误";
        BindingResult bindingResult = null;
        if (ex instanceof MethodArgumentNotValidException methodArgumentNotValidException) {
            bindingResult = methodArgumentNotValidException.getBindingResult();
        }
        if (ex instanceof BindException bindException) {
            bindingResult = bindException.getBindingResult();
        }
        if (ex instanceof ConstraintViolationException constraintViolationException) {
            errorMsg = constraintViolationException.getMessage();
        }
        if (bindingResult != null) {
            for (FieldError filedError : bindingResult.getFieldErrors()) {
                errorMsg = filedError.getDefaultMessage() + ":[" + filedError.getField() + "]";
            }
        }
        if (log.isErrorEnabled()) {
            log.error(EXCEPTION_MESSAGE, ex);
        }
        return ResponseBean.error(ErrorEnum.SERVER_ERROR.getErrorCode(), errorMsg);
    }
    
    /**
     * Controller上一层相关异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler({NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class, MissingPathVariableException.class,
            MissingServletRequestParameterException.class, TypeMismatchException.class,
            HttpMessageNotReadableException.class, HttpMessageNotWritableException.class,
            HttpMediaTypeNotAcceptableException.class, ServletRequestBindingException.class,
            ConversionNotSupportedException.class, MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class, UnexpectedTypeException.class,
            FileNotFoundException.class, HttpMessageConversionException.class})
    public ResponseBean<String> handleServletException(Exception e) {
        if (log.isErrorEnabled()) {
            log.error(EXCEPTION_MESSAGE, e);
        }
        return ResponseBean.error(ErrorEnum.SERVER_ERROR.getErrorCode(), e.getMessage());
    }
}
